#include <sstream>
#include <set>
#include <algorithm>
#include <numeric>
#include <exception>

#include "manhatline.h"
#include "cell.h"
#include "line.h"
#include "grid.h"


segment_t const cell_t::default_seg = segment_t(point_t(), point_t());

cell_t::cell_t() : center_(0, 0), center_in_(false)
{
}

cell_t::cell_t(point_t const & center) : center_(center), center_in_(false)
{
}

point_t const & cell_t::center() const
{
    return center_;
}

bool cell_t::center_in() const
{
    return center_in_;
}

void cell_t::center_in(bool flag)
{
    center_in_ = flag;
}

bool cell_t::empty() const
{
    return segments_.empty();
}

void cell_t::clear()
{
    segments_.clear();
    center_in_ = false;
}

size_t cell_t::size() const
{
    return segments_.size();
}

segment_t const & cell_t::operator[] (size_t idx) const
{
    assert(idx < segments_.size());
    return segments_[idx];
}

segment_t & cell_t::operator [](size_t idx)
{
    assert(idx < segments_.size());
    return segments_[idx];
}

segment_t cell_t::min_segment(manhattan_line_t const & m_line, manhattan_part_t part) const
{
    if(segments_.empty())
        return default_seg;
    std::vector<segment_t>::const_iterator i = segments_.begin();
    std::vector<segment_t>::const_iterator min = segments_.begin();

    double min_distance = -1;
    less_by_angle less(m_line.part(part));
    for(; i != segments_.end(); ++i)
    {
        double dist = m_line.intersects(*i, part);
        if(dist != -1)
        {
            if( (min_distance > dist) ||
                (dist == min_distance && less(*i, *min)) ||
                (min_distance < 0) )
            {
                min_distance = dist;
                min = i;
            }
        }
    }

    if(min_distance < 0)
        return default_seg;
    return *min;
}

void inserter::operator() (cell_t & cell, segment_t const & segment) const
{
    if(std::find(cell.segments_.begin(), cell.segments_.end(), segment) == cell.segments_.end())
        cell.segments_.push_back(segment);
}

void inserter_without_intersections::operator() (cell_t & cell, segment_t const & segment) const
{
    bool flag = false;
    for(size_t i = 0; i < cell.size(); ++i)
    {
        if(segment == cell[i])
        {
            flag = true;
            break;
        }
        else if(segment.first != cell[i].first && segment.second != cell[i].first
                && segment.first != cell[i].second && segment.second != cell[i].second)
        {
            if(intersects(segment, cell[i]))
            {
                throw self_intersection_exc();
            }
        }
    }
    
    if(!flag)
        cell.segments_.push_back(segment);
}

std::ostream & operator<< (std::ostream & out, cell_t const & cell)
{
    out << cell.center() << "; ";
    std::vector<segment_t>::const_iterator i = cell.segments_.begin();
    for(; i != cell.segments_.end(); ++i)
    {
        out << i->first << "_" << i->second << ";  ";
    }
    return out;
}

std::istream & operator>> (std::istream & in, cell_t & cell)
{
    cell = cell_t();
    
    std::string buff;
    std::getline(in, buff);
    std::stringstream ss(buff);

    char delim1, delim2;
    ss >> cell.center_ >> delim2;

    point_t first, second;
    while (ss >> first)
    {
        ss >> delim1 >> second >> delim2;
        if(delim1 != '_' && delim2 != ';') return in;
        cell.segments_.push_back(segment_t(first, second));
    }
    return in;
}

bool operator== (cell_t const & left, cell_t const & right) 
{
    if(left.center_ != right.center_ || left.segments_.size() != right.segments_.size())
        return false;
    if(left.empty())
        return true;
    std::vector<segment_t>::const_iterator i = left.segments_.begin();
    std::vector<segment_t>::const_iterator j = right.segments_.begin();
    for(; i != left.segments_.end(); ++i, ++j)
    {
        /*if(right.segments_.find(*i) == right.segments_.end())
            return false;*/
        if(*i != *j &&
                std::find(right.segments_.begin(), right.segments_.end(), *i) == right.segments_.end())
            return false;
    }
    return true;
}

bool operator!= (cell_t const & left, cell_t const & right)
{
    return !(left == right);
}

self_intersection_exc::self_intersection_exc() throw() 
        : exc_(std::string("Polygon has self-intersections"))
{
}

char const * self_intersection_exc::what() const throw()
{
    return exc_.c_str();
}

self_intersection_exc::~self_intersection_exc() throw()
{
}
